package online.struts.chain.command;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import online.filter.RecordURIFilter;
import online.model.UniModelImpl;
import online.model.session.ActionParameter;
import online.model.session.SessionUser;
import online.struts.action.UniForm;
import online.struts.chain.command.ActionRequest.CommandRequest;
import online.struts.mapping.RequestMapping;
import online.view.ViewUtil;
import online.view.model.ViewMap;

import org.apache.struts.chain.commands.ActionCommandBase;
import org.apache.struts.chain.contexts.ActionContext;
import org.apache.struts.chain.contexts.ServletActionContext;

import core.config.Factory;

/**
 * ビューコマンドクラス
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class ViewProcessor extends ActionCommandBase implements CommandRequest {

	/**
	 * @see org.apache.struts.chain.commands.ActionCommandBase
	 * #execute(org.apache.struts.chain.contexts.ActionContext)
	 */
	@Override
	public boolean execute(final ActionContext context) throws Exception {
		return ActionRequest.execute(context, this);
	}

	/**
	 * コマンド処理
	 *
	 * @param sac サーブレットアクションコンテキスト
	 * @param mapping アクションマッピング
	 * @param form 汎用フォーム
	 * @return 処理結果
	 */
	@Override
	public boolean command(final ServletActionContext sac,
			final RequestMapping mapping, final UniForm form) {

		if (sac.getResponse().isCommitted()) {
			return false;
		}

		if (!RequestMapping.isJsp(sac.getForwardConfig())) {
			return false;
		}

		// 確認画面以外の場合セションに残らないようにする
		if (mapping.isInSessionEntry()
				&& !mapping.isSessionExit(form.getActionParameter().getAid())) {
			if (sac.getResponse().getStatus() == 0) {
				sac.getResponse().setStatus(HttpServletResponse.SC_OK);
			}
		}

		// 汎用データマップ設定
		sac.getRequest().setAttribute(ViewUtil.ATTR_MAP,
				createViewMap(sac.getResponse(), createViewForm(sac, mapping, form)));

		sac.getRequest().removeAttribute(mapping.getName());

		return false;
	}

	/**
	 * ビューマップ取得
	 *
	 * @param response レスポンス
	 * @param uf 汎用フォーム
	 * @return ビューマップ
	 */
	private ViewMap createViewMap(final HttpServletResponse response, final UniForm uf) {
		ViewMap vm = Factory.create(ViewMap.class);
		vm.setMap(uf.toMap());
		// jspへ行ってから、エンコードもタイプも分かるので、responseを設定しておく。
		vm.setResponse(response);
		return vm;
	}

	/**
	 * Veiw用フォーム作成
	 * @param sac サーブレットコンテキスト
	 * @param mapping マッピング
	 * @param uf 汎用フォーム
	 * @return View用フォーム
	 */
	private UniForm createViewForm(final ServletActionContext sac,
			final RequestMapping mapping, final UniForm uf) {
		HttpServletRequest request = sac.getRequest();

		UniForm form = new UniForm();
		form.reset(mapping, request);
		form.populate(new UniModelImpl());
		form.setDateTime(uf.getDateTime());
		form.setSessionUser(uf.getSessionUser());
		form.setActionParameter(uf.getActionParameter());

		setItemValues(form, uf, mapping);

		doView(sac, form);

		// jsp 埋め込み アクセス用
		form.setValue("AID", uf.getActionParameter().getAid());
		form.setValue("DATE_TIME", uf.getDateTime());
		form.setValue("IP", uf.getActionParameter().getIp());
		form.setValue(ActionParameter.TAG_TOKEN, uf.getActionParameter().getToken());

		form.setValue("PATH", mapping.getPath().substring("/".length()));
		form.setValue("ACTION_PATH", mapping.getPath().substring("/".length()) + ".do");
		form.setValue("METHOD", request.getMethod());

		setSessionUser(form);

		form.setValue(RecordURIFilter.CONTEXT_PATH, RecordURIFilter.getContextPath(request));
		form.setValue(RecordURIFilter.REQUEST_PATH, RecordURIFilter.getRequestPath(request));
		form.setValue(RecordURIFilter.REQUEST_URI, RecordURIFilter.getRequestUri(request));
		form.setValue(RecordURIFilter.REQUEST_QUERY, RecordURIFilter.getRequestQuery(request));
		form.setValue(RecordURIFilter.RESPONSE_PATH, RecordURIFilter.getResponsePath(request));
		form.setValue(RecordURIFilter.RESPONSE_URI, RecordURIFilter.getResponseUri(request));
		form.setValue(RecordURIFilter.RESPONSE_QUERY, RecordURIFilter.getResponseQuery(request));

		return form;
	}

	/**
	 * セションユーザ情報設定
	 * @param form View用フォーム
	 */
	private void setSessionUser(final UniForm form) {
		SessionUser su = form.getSessionUser();
		if (su != null) {
			form.setValue("Uid", su.getUid());
			for (final Entry<String, String> me : su.getAttributeMap().entrySet()) {
				form.setValue(me.getKey(), me.getValue());
			}
		}
	}

	/**
	 * 項目値設定
	 * @param to 設定先
	 * @param from 設定元
	 * @param rm マッピング
	 */
	private void setItemValues(final UniForm to, final UniForm from, final RequestMapping rm) {
		if (from.getActionParameter().isRollbacked() || from.isReverse()) {
			to.putAll(from);
			from.getActionParameter().setParameterTo(to);

			from.copyValueTo(to, from.getRollbackTo());
			from.getActionParameter().setParameterTo(to.getModel(from.getRollbackTo()));
		} else {
			from.getActionParameter().setParameterTo(to);
			to.putAll(from);
		}

		for (final Entry<String, String> me
				: Factory.<Map<String, String>>cast(rm.getProperties()).entrySet()) {
			to.setValue(me.getKey(), me.getValue());
		}
	}

	/**
	 * View処理
	 * @param sac サーブレットアクションコンテキスト
	 * @param uf 汎用フォーム
	 */
	private void doView(final ServletActionContext sac, final UniForm uf) {
		if (sac.getAction() != null) {
			Method m = Factory.getMethod(sac.getAction().getClass(), "view", UniForm.class);
			if (m != null && m.getReturnType() == void.class) {
				Factory.invoke(sac.getAction(), m, uf);
			}
		}
	}
}
